# -*-coding:utf-8-*-
import pandas as pd

pd.set_option('display.unicode.east_asian_width', True)
df: pd.DataFrame = pd.read_excel(r'..\book.xlsx', sheet_name=0)

"""
groupby(by, axis=0, level=None, as_index=True, sort=True, group_keys=True,
        squeeze=no_default, observed=False, dropna=True) -> DataFrameGroupBy
    -by：分组依据，可以是列名/列名列表/字典/函数
    -axis：0-按行分组。1-按列分组
    -as_index：是否将分组列名作为输出的索引，默认为True；当设置为False时相当于加了reset_index功能
    -sort：与SQL中groupby操作会默认执行排序一致，该groupby也可通过sort参数指定是否对输出结果按索引排序
    -group_keys：调用apply函数时，是否将分组的键添加到索引以标识片段
"""

"""1.基本用法
>>> df_sum
            图书编号   单价    销量
分类                             
ASP.NET  15137020644  149.6   240
...
Web      15138423169  159.6   778

>>> df_sum2
         销量   单价
分类                
ASP.NET   240  149.6
...
Web       778  159.6

>>> df_sum3
分类
ASP.NET     240
...
Web         778
Name: 销量, dtype: int64

>>> df_sum4
      分类  销量
0  ASP.NET   240
...
8      Web   778
"""
df_sum = df.groupby('分类').sum()  # groupby中的sum等数值计算函数会忽略非数字类型的列
df_sum2 = df.groupby('分类')[['销量', '单价']].sum()  # 指定哪几列数据进行聚合
df_sum3 = df.groupby('分类')['销量'].sum()  # 指定单个列数据进行聚合（as_index=True时返回series）
df_sum4 = df.groupby('分类', as_index=False)['销量'].sum()  # 指定单个列数据进行聚合（as_index=False时返回df）

"""2.对分组数据进行迭代
>>> 输出
ASP.NET
   序号    图书编号                书名  单价  销量     分类
21  B03  7567799424     ASP.Net project  69.8   120  ASP.NET
22  B17  7569221220  Zero basic ASP.NET  79.8   120  ASP.NET
Android
  序号    图书编号                  书名  单价  销量     分类
0  B01  7569204537   Android Programming  89.8  1300  Android
1  B02  7567787421  Professional Android  59.8  2355  Android
2  B16  7569208542    Head First Android  89.8   110  Android
...
"""
for key, group in df.groupby('分类'):
    # key为分组依据的值（如何分类依据有多个的话为key1,key2...），group为分组后的子df
    # print(key)
    # print(group)
    pass

"""3.聚合函数agg
>>> df_agg1
                           图书编号                单价               销量             
                 sum          mean    sum        mean   sum         mean
分类                                                                    
ASP.NET  15137020644  7.568510e+09  149.6   74.800000   240   120.000000
...
Web      15138423169  7.569212e+09  159.6   79.800000   778   389.000000

>>> df_agg2
          单价              销量
           sum        mean   sum
分类                            
ASP.NET  149.6   74.800000   240
...
Web      159.6   79.800000   778
"""
df_agg1 = df.groupby('分类').agg(['sum', 'mean'])  # 分组统计各列的平均值、总和
df_agg2 = df.groupby('分类').agg({'单价': ['sum', 'mean'], '销量': 'sum'})  # 指定要要聚合的列和其使用的聚合函数

"""transform函数
transform是一种什么数据操作，和agg有什么区别呢？
在上面的agg中，实现了按分类统计销量的平均值等操作，如果现在需要在原数据集中新增一列’销量平均值‘，代表
各类书籍平均销量（相同分类的书记具有相同的平均销量），这时用transform函数就可以直接实现这个需求
>>> df2
   序号    图书编号                    书名   单价  销量     分类   销量平均值
0   B01  7569204537     Android Programming   89.8  1300  Android  1255.000000
1   B02  7567787421    Professional Android   59.8  2355  Android  1255.000000
2   B16  7569208542      Head First Android   89.8   110  Android  1255.000000
...
23  B09  7567787438        Struts in Action   69.8   129     Java   254.600000
24  B12  7567790315      Spring in Practice   69.8   120     Java   254.600000
25  B13  7567790971            PHP Cookbook   69.8   354      PHP   301.000000
26  B24  7569208689          Zero basic PHP   79.8   248      PHP   301.000000
"""
df2 = df.copy()
df2['销量平均值'] = df2.groupby('分类')['销量'].transform('mean')

"""apply函数
groupby中的apply执行过程为：以分组后的子DataFrame作为参数传入指定函数的，最后将执行结果合并，基本操作单位是DataFrame
注意：虽然说apply拥有更大的灵活性，但apply的运行效率会比agg和transform更慢。所以groupby之后能用
agg和transform解决的问题还是优先使用这两个方法，解决不了才考虑使用apply。
>>> df_first
        序号    图书编号                    书名  单价  销量     分类
分类                                                                 
ASP.NET  B03  7567799424         ASP.Net project  69.8   120  ASP.NET
Android  B01  7569204537     Android Programming  89.8  1300  Android
C#       B04  7569210453        Beginning.Csharp  89.8   120       C#
C++      B06  7567787445              C++ Primer  69.8   120      C++
Java     B10  7569206081        thinking in java  79.8   241     Java
PHP      B13  7567790971            PHP Cookbook  69.8   354      PHP
Python   B15  7569222258            Data Science  79.8   888   Python
SQL      B23  7569212693  Zero foundation Oracle  79.8   148      SQL
Web      B20  7569212709   Zero basic HTML5+CSS3  79.8   456      Web
"""
df_first = df.groupby('分类').apply(lambda d: d.iloc[0, :])  # 分组后获取每组第一行的数据
